﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Mvc;
using System.Web.Security;
using System.Security.Principal;

namespace DoeLibsMVC.Security
{

    //http://kevin-junghans.blogspot.se/2013/02/mixing-forms-authentication-basic.html

    [AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class BasicAuthorizeAttribute : System.Web.Http.AuthorizeAttribute
    {

        private enum AuthType { basic, cookie };

        private string DecodeFrom64(string encodedData)
        {

            byte[] encodedDataAsBytes = System.Convert.FromBase64String(encodedData);
            string returnValue = System.Text.Encoding.ASCII.GetString(encodedDataAsBytes);

            return returnValue;
        }

        private bool GetUserNameAndPassword(HttpActionContext actionContext, out string username, out string password, out AuthType authType)
        {
            authType = AuthType.basic;
            bool gotIt = false;
            username = string.Empty;
            password = string.Empty;
            IEnumerable<string> headerVals = null;
            if (actionContext.Request.Headers.TryGetValues("Authorization", out headerVals))
            {
                try
                {
                    string authHeader = headerVals.FirstOrDefault();
                    char[] delims = { ' ' };
                    string[] authHeaderTokens = authHeader.Split(new char[] { ' ' });
                    if (authHeaderTokens[0].Contains("Basic"))
                    {
                        string decodedStr = DecodeFrom64(authHeaderTokens[1]);
                        string[] unpw = decodedStr.Split(new char[] { ':' });
                        username = unpw[0];
                        password = unpw[1];
                    }
                    else
                    {
                        if (authHeaderTokens.Length > 1)
                        {
                            username = DecodeFrom64(authHeaderTokens[1]);
                        }
                            
                        authType = AuthType.cookie;
                    }

                    gotIt = true;
                }
                catch 
                { 
                    gotIt = false; 
                }
            }

            return gotIt;
        }

        /// <summary>
        /// get credentials from the HTTP header and returns if they are correct
        /// </summary>
        /// <param name="actionContext"></param>
        /// <param name="username"></param>
        /// <returns></returns>
        private bool Authenticate(HttpActionContext actionContext, out string username)
        {
            bool isAuthenticated = false;
            username = string.Empty;
            string password;
            AuthType authenticationType;

            if (GetUserNameAndPassword(actionContext, out username, out password, out authenticationType))
            {
                if (authenticationType == AuthType.basic)
                {
                    //check username and password and set logincookie and System.Web.HttpContext.Current.User

                    if (SecurityHelper.login(username, password) == SecurityHelper.LoginStatus.OK)
                    {
                        isAuthenticated = true;
                    }
                    //else
                    //{
                    //    WebSecurity.Logout();
                    //}
                }
                else //authType == cookie
                {

                    //check if FormCookie is set. Get userId from it and set HttpContext.User
                    string valueOfAuthCookie = SecurityHelper.getAuthCookieValue();

                    if (!string.IsNullOrEmpty(valueOfAuthCookie))
                    {
                        if (valueOfAuthCookie == username) //the username is in case of the cookie the userId
                        {
                            CustomPrincipal user = new CustomPrincipal(Convert.ToInt32(valueOfAuthCookie));
                            HttpContext.Current.User = user;

                            isAuthenticated = true;
                        }
                        else
                        {
                            isAuthenticated = false;
                        }
                    }
                    else
                    {
                        isAuthenticated = false;
                    }
                        

                    //if (WebSecurity.IsAuthenticated)
                    //{
                    //    isAuthenticated = true;
                    //}

                    //username = WebSecurity.CurrentUserName;
                }
            }
            else
            {
                actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
            }

            return isAuthenticated;
        }

        private bool isAuthorized(string username)
        {
            bool authorized = false;

            //var roles = (SimpleRoleProvider)System.Web.Security.Roles.Provider;
            //authorized = roles.IsUserInRole(username, this.Roles);

            if (System.Web.HttpContext.Current.User != null)
            {
                authorized = System.Web.HttpContext.Current.User.IsInRole(this.Roles);
            }

            return authorized;
        }

        //OnAuthorization(AuthorizationContext filterContext);
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            string username;

            if (Authenticate(actionContext, out username))
            {
                if (!isAuthorized(username))
                {
                    actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
                }
            }
            else
            {
                actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
            }
        }
    }
}